using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Threading;


    namespace VirtualDeivce
    {
        /// <summary>
        /// VMouse is a class to simulate the mouse operations
        /// 
        /// Functions:
        /// 1) Click
        /// 2) Double Click
        /// 3) Wheel
        /// 4) Mouse Down
        /// 5) Mouse Up
        /// 6) MoveTo
        /// 7) Move
        /// </summary>
        public class VMouse
        {
            public enum ButtonType
            { 
                Left, Middle, Right
            }

            public enum Direction
            {
                Left, Right, Up, Down
            }

            private enum MouseEvent : uint
            {
                Move = 0x0001,
                LeftDown = 0x0002,
                LeftUp = 0x0004,
                RightDown = 0x0008,
                RightUp = 0x0010,
                MiddleDown = 0x0020,
                MiddleUp = 0x0040,
                XDown = 0x0080,
                XUp = 0x0100,
                Wheel = 0x0800,
                VirtualDesk = 0x4000,
                Absolute = 0x8000
            }

            const uint WHEEL_DELTA = 120;


            #region methods

            public VMouse()
            {
            }

            /// <summary>
            /// move mouse pointer to the current postion
            /// </summary>
            /// <param name="pt"></param>
            public void MoveTo(Point pt)
            {
                MoveTo(pt.X, pt.Y);
            }

            public void MoveTo(int x, int y)
            {
                SetCursorPos(x, y);
            }

            public void Move(Direction dirc)
            {
                Point curPt;
                if (GetCursorPos(out curPt))
                {
                    switch (dirc)
                    {
                        case Direction.Left:
                            SetCursorPos(curPt.X - 1, curPt.Y);
                            break;

                        case Direction.Right:
                            SetCursorPos(curPt.X + 1, curPt.Y);
                            break;

                        case Direction.Down:
                            SetCursorPos(curPt.X, curPt.Y + 1);
                            break;

                        case Direction.Up:
                            SetCursorPos(curPt.X, curPt.Y - 1);
                            break;
                    }
                }
            }

            /// <summary>
            /// click operate on the current postion
            /// </summary>
            public void Click()
            {
                Click(ButtonType.Left);
            }

            public void Click(ButtonType type)
            {
                switch (type)
                {
                    case ButtonType.Left:
                        mouse_event(MouseEvent.LeftDown, 0, 0, 0, UIntPtr.Zero);
                        mouse_event(MouseEvent.LeftUp, 0, 0, 0, UIntPtr.Zero);
                        break;

                    case ButtonType.Middle:
                        mouse_event(MouseEvent.MiddleDown, 0, 0, 0, UIntPtr.Zero);
                        mouse_event(MouseEvent.MiddleUp, 0, 0, 0, UIntPtr.Zero);
                        break;

                    case ButtonType.Right:
                        mouse_event(MouseEvent.RightDown, 0, 0, 0, UIntPtr.Zero);
                        mouse_event(MouseEvent.RightUp, 0, 0, 0, UIntPtr.Zero);
                        break;
                }
            }


            /// <summary>
            /// double click operate on the current postion
            /// </summary>
            public void DoubleClick()
            {
                DoubleClick(ButtonType.Left);
            }

            public void DoubleClick(ButtonType type)
            {
                Click(type);
                Click(type);
            }

            /// <summary>
            /// wheel the mouse wheel
            /// </summary>
            /// <param name="num"> 
            /// A positive value indicates that the wheel was rotated forward, away from the user; 
            /// a negative value indicates that the wheel was rotated backward, toward the user
            /// </param>
            public void Wheel(uint num)
            {
                mouse_event(MouseEvent.Wheel, 0, 0, num*WHEEL_DELTA, UIntPtr.Zero);
            }

            public void MouseDown()
            {
                MouseDown(ButtonType.Left);
            }

            public void MouseDown(ButtonType type)
            {
                switch (type)
                {
                    case ButtonType.Left:
                        mouse_event(MouseEvent.LeftDown, 0, 0, 0, UIntPtr.Zero);
                        break;

                    case ButtonType.Middle:
                        mouse_event(MouseEvent.MiddleDown, 0, 0, 0, UIntPtr.Zero);
                        break;

                    case ButtonType.Right:
                        mouse_event(MouseEvent.RightDown, 0, 0, 0, UIntPtr.Zero);
                        break;
                }
            }

            public void MouseUp()
            {
                MouseUp(ButtonType.Left);
            }

            public void MouseUp(ButtonType type)
            {
                switch (type)
                {
                    case ButtonType.Left:
                        mouse_event(MouseEvent.LeftUp, 0, 0, 0, UIntPtr.Zero);
                        break;

                    case ButtonType.Middle:
                        mouse_event(MouseEvent.MiddleUp, 0, 0, 0, UIntPtr.Zero);
                        break;

                    case ButtonType.Right:
                        mouse_event(MouseEvent.RightUp, 0, 0, 0, UIntPtr.Zero);
                        break;
                }
            }
            #endregion


            #region dll import
            [DllImport("User32.dll")]
            private static extern void mouse_event(MouseEvent me, int dx, int dy, uint data, UIntPtr extraInfo);

            [DllImport("User32.dll")]
            private static extern bool SetCursorPos(int x, int y);

            [DllImport("User32.dll")]
            private static extern bool GetCursorPos(out Point pt);
            #endregion
        }

        /// <summary>
        /// VKeyboard is a class to simulate the keyboard operations
        /// 
        /// Functions:
        /// 1) KeyDown
        /// 2) ComposeKeyDown
        /// 3) KeyUp
        /// 4) KeyPress
        /// 5) SendKeys
        /// </summary>
        public class VKeyboard
        {
            const uint KEYEVENTF_EXTENDEDKEY = 0x1;
            const uint KEYEVENTF_KEYUP = 0x2;
            const uint KEYDOWN = 0x00;

            #region methods
            public VKeyboard()
            {
                if (_keyDic.Count == 0)
                {
                    _keyDic.Add("{Ctrl}", 0x11);
                    _keyDic.Add("{Shift}", 0x10);
                    _keyDic.Add("{Alt}", 0x12);
                    _keyDic.Add("{Enter}", 0x0D);
                    _keyDic.Add("{Backspace}", 0x08);
                    _keyDic.Add("{Tab}", 0x09);
                    _keyDic.Add("{CapsLock}", 0x14);
                    _keyDic.Add("{Esc}", 0x1B);
                    _keyDic.Add("{Space}", 0x20);

                    _keyDic.Add("{Left}", 0x25);
                    _keyDic.Add("{Up}", 0x26);
                    _keyDic.Add("{Right}", 0x27);
                    _keyDic.Add("{Down}", 0x28);

                    _keyDic.Add("{Menu}", 0xA5);
                    _keyDic.Add("{Win}", 0x5B);

                    _keyDic.Add("{F1}", 0x70);
                    _keyDic.Add("{F2}", 0x71);
                    _keyDic.Add("{F3}", 0x72);
                    _keyDic.Add("{F4}", 0x73);
                    _keyDic.Add("{F5}", 0x74);
                    _keyDic.Add("{F6}", 0x75);
                    _keyDic.Add("{F7}", 0x76);
                    _keyDic.Add("{F8}", 0x77);
                    _keyDic.Add("{F9}", 0x78);
                    _keyDic.Add("{F10}", 0x79);
                    _keyDic.Add("{F11}", 0x7A);
                    _keyDic.Add("{F12}", 0x7B);

                    _keyDic.Add("{Insert}", 0x2D);
                    _keyDic.Add("{Home}", 0x24);
                    _keyDic.Add("{PgUp}", 0x21);
                    _keyDic.Add("{PgDn}", 0x22);
                    _keyDic.Add("{End}", 0x23);
                    _keyDic.Add("{Delete}", 0x2E);

                    _keyDic.Add("{ScreenShot}", 0x2C);
                    _keyDic.Add("{ScrollLock}", 0x91);
                    _keyDic.Add("{Break}", 0x13);

                    _keyDic.Add("{NumLock}", 0x90);
                    //_keyDic.Add("{0}", 0x60);
                    //_keyDic.Add("{1}", 0x61);
                    //_keyDic.Add("{2}", 0x62);
                    //_keyDic.Add("{3}", 0x63);
                    //_keyDic.Add("{4}", 0x64);
                    //_keyDic.Add("{5}", 0x65);
                    //_keyDic.Add("{6}", 0x66);
                    //_keyDic.Add("{7}", 0x67);
                    //_keyDic.Add("{8}", 0x68);
                    //_keyDic.Add("{9}", 0x69);
                    //_keyDic.Add("{*}", 0x6A);
                    //_keyDic.Add("{+}", 0x6B);
                    //_keyDic.Add("{-}", 0x6D);
                    //_keyDic.Add("{/}", 0x6F);                    
                }
            }

            public void KeyDown(string keyVal)
            {
                byte vk;
                if (MapKeyToASCII(keyVal, out vk) || MapKey(keyVal, out vk))
                {
                    KeyDown(vk);
                }
            }

            public void KeyDown(byte vk)
            {
                if (GetKeyState((int)vk) != 1)
                {
                    keybd_event(vk, 0, 0, 0);
                    System.Threading.Thread.Sleep(100);
                }
             }

            public void ComposeKeyDown(string[] keyVals)
            {
                foreach (string keyVal in keyVals)
                {
                    byte vk;
                    if (MapKey(keyVal, out vk) || MapKeyToASCII(keyVal, out vk))
                    {
                        KeyDown(vk);
                    }
                }

                foreach (string keyVal in keyVals)
                {
                    byte vk;
                    if (MapKey(keyVal, out vk) || MapKeyToASCII(keyVal, out vk))
                    {
                        KeyUp(vk);
                    }
                }
            }
          

            public void KeyUp(string keyVal)
            {
                byte vk;
                if (MapKeyToASCII(keyVal, out vk) || MapKey(keyVal, out vk))
                {
                    KeyUp(vk);
                }
            }

            public void KeyUp(byte vk)
            {
                if (GetKeyState((int)vk) == 1)
                {
                    keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
                    System.Threading.Thread.Sleep(100);
                }
            }


            public void KeyPress(string keyStr)
            {
                byte vk;
                if (MapKeyToASCII(keyStr, out vk) || MapKey(keyStr, out vk))
                    KeyPress(vk);
            }

            public void KeyPress(byte vk)
            {
                KeyDown(vk);
                KeyUp(vk);
            }

            /// <summary>
            /// this function is a utility function for send mutli char
            /// </summary>
            /// <param name="str"></param>
            public void SendKeys(string str)
            {
                ArrayList vkArr = SplitKeyValue(str);
                if (vkArr.Count > 0)
                {
                    foreach (object vk in vkArr)
                        KeyPress((byte)vk);
                }
            }

            #endregion

            #region dll import
            [DllImport("User32.dll")]
            static private extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtroInfo);

            [DllImport("User32.dll")]
            static private extern short GetKeyState(int nVk);
            #endregion

            /// <summary>
            /// map the string to virtual key value
            /// </summary>
            /// <param name="keyVal">string key value</param>
            /// <param name="bVk">virtual key value</param>
            /// <returns></returns>
            private bool MapKey(string keyVal, out byte bVk)
            {
                return _keyDic.TryGetValue(keyVal, out bVk);
            }

            private bool MapKeyToASCII(string keyVal, out byte bVk)
            {
                if (keyVal.Length == 1)
                {
                    char[] ch = keyVal.ToCharArray();
                    bVk = (byte)ch[0];
                    return true;
                }
                else
                {
                    bVk = 0xFF;
                    return false;
                }
            }

            /// <summary>
            /// split the string to virtual key array
            /// the string maybe include the system
            /// </summary>
            /// <param name="keyStr"></param>
            /// <returns>virtual key array , byte type</returns>
            private ArrayList SplitKeyValue(string keyStr)
            {
                ArrayList keyArray = new ArrayList();
                bool sysKeyFlag = false;
                StringBuilder sysKey = new StringBuilder();

                foreach (char ch in keyStr.ToCharArray())
                {
                    if (ch == '{' && !sysKeyFlag)
                    {
                        sysKeyFlag = true;
                        sysKey.Append(ch);
                    }
                    else if (ch == '{' && sysKeyFlag)  // case  "{aa{", we need add "{aa" to array
                    {
                        string sysKeyStr = sysKey.ToString();

                        foreach (char innerCh in sysKeyStr.ToCharArray())
                            keyArray.Add((byte)innerCh);
                        
                        sysKey.Remove(0, sysKey.Length);
                        sysKey.Append(ch);
                    }
                    else if (ch == '}' && sysKeyFlag) // case "{Ctrl}"
                    {
                        sysKeyFlag = false;
                        sysKey.Append(ch);

                        // there is a system key as "{Ctrl}"
                        string sysKeyStr = sysKey.ToString();
                        byte vk;
                        if (MapKey(sysKeyStr, out vk))
                        {
                            keyArray.Add(vk);
                        }
                        else
                        {
                            // case "{abcd}"
                            foreach (char innerCh in sysKeyStr.ToCharArray())
                                keyArray.Add((byte)innerCh);
                        }

                        sysKey.Remove(0, sysKey.Length);
                    }
                    else
                    {
                        if (sysKeyFlag)
                            sysKey.Append(ch);
                        else
                            keyArray.Add((byte)ch);
                    }
                }

                return keyArray;
            }

            static private Dictionary<string, byte> _keyDic = new Dictionary<string,byte>();
        }
    }
